/*
 * DefaultTableModel.java  0.4.0 / Oct 25, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing.table;

import java.util.Vector;

import com.cute4j.swing.events.TableModelEvent;


/**
 * Default Table Model.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class DefaultTableModel extends AbstractTableModel {

// Protected Fields. -----------------------------------------------------------

  // TODO: Try (consider) to replace Vector with ArrayList */

  protected Vector dataVector; // NOPMD
  protected Vector columnIdentifiers; // NOPMD

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new DefaultTableModel object.
   *
   * @param data the data
   * @param columnNames the column names
   */
  public DefaultTableModel(Vector data, Vector columnNames) { // NOPMD
    setDataVector(data, columnNames); // NOPMD
  }

  /**
   * Constructs new DefaultTableModel object.
   *
   * @param data the data
   * @param columnNames the column names
   */
  public DefaultTableModel(Object[][] data, Object[] columnNames) {
    this(convertToVector(data), convertToVector(columnNames));
  }

  /**
   * Constructs new DefaultTableModel object.
   *
   * @param columnNames the column names
   * @param numRows the number of rows
   */
  public DefaultTableModel(Vector columnNames, int numRows) { // NOPMD
    if (numRows < 0) {
      throw new IllegalArgumentException("Negative numRows");
    }
    int numColumns = 0;
    if (columnNames != null) {
      numColumns = columnNames.size();
    }
    Vector data = new Vector(numColumns); // NOPMD
    for (int i = numRows - 1; i >= 0; i--) { // NOPMD
      Vector rowData = new Vector(numColumns); // NOPMD
      rowData.setSize(numColumns);
      data.add(rowData);
    }
    setDataVector(data, columnNames); // NOPMD
  }

  /**
   * Constructs new DefaultTableModel object.
   *
   * @param columnNames the column names
   * @param numRows the number of rows
   */
  public DefaultTableModel(Object[] columnNames, int numRows) {
    this(convertToVector(columnNames), numRows);
  }

  /**
   * Constructs new DefaultTableModel object.
   *
   * @param numRows the number of rows
   * @param numColumns the number of columns
   */
  public DefaultTableModel(int numRows, int numColumns) {
    Vector defaultNames = new Vector(numColumns); // NOPMD
    Vector data = new Vector(numRows); // NOPMD
    for (int i = 0; i < numColumns; i++) {
      defaultNames.add(super.getColumnName(i));
    }
    for (int i = numRows - 1; i >= 0; i--) {
      Vector vector = new Vector(numColumns); // NOPMD
      vector.setSize(numColumns);
      data.add(vector);
    }
    setDataVector(data, defaultNames); // NOPMD
  }

  /**
   * Constructs new DefaultTableModel object.
   */
  public DefaultTableModel() {
    this(0, 0);
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the number of columns.
   *
   * @return the number of columns
   */
  public int getColumnCount() {
    if (columnIdentifiers != null) {
      return columnIdentifiers.size();
    }
    return 0;
  }

  /**
   * Sets columns count.
   *
   * @param columnCount the column count.
   */
  public void setColumnCount(int columnCount) {
    int dvs = dataVector.size();
    for (int i = 0; i < dvs; i++) {
      ((Vector)dataVector.get(i)).setSize(columnCount); // NOPMD
    }
    if (columnIdentifiers != null) {
      columnIdentifiers.setSize(columnCount);
    }
    fireTableStructureChanged();
  }

  /**
   * Returns column name.
   *
   * @param column the column index
   *
   * @return column name
   */
  public String getColumnName(int column) {
    if ((columnIdentifiers != null) && (column < getColumnCount())) {
      Object cid = columnIdentifiers.get(column);
      if (cid != null) {
        return cid.toString();
      }
    }
    return super.getColumnName(column);
  }

  /**
   * Returns row count.
   *
   * @return row count
   */
  public int getRowCount() {
    return dataVector.size();
  }

  /**
   * Sets the row count.
   *
   * @param rowCount the row count
   */
  public void setRowCount(int rowCount) {
    int dvs = dataVector.size();
    if (rowCount < dvs) {
      dataVector.setSize(rowCount);
      fireTableRowsDeleted(rowCount, dvs - 1);
    } else {
      for (int i = rowCount - dvs; i >= 0; i--) {
        Vector emptyVector = new Vector(); // NOPMD
        emptyVector.setSize(columnIdentifiers.size());
        dataVector.add(emptyVector);
      }
      fireTableRowsInserted(dvs, rowCount - 1);
    }
  }

  /**
   * Returns data vector.
   *
   * @return data vector
   */
  public Vector getDataVector() { // NOPMD
    return dataVector;
  }

  /**
   * Sets data vector.
   *
   * @param data the data
   * @param columnNames the column names
   */
  public void setDataVector(Vector data, Vector columnNames) { // NOPMD
    if (data == null) {
      dataVector = new Vector(); // NOPMD
    } else {
      dataVector = data;
    }
    setColumnIdentifiers(columnNames);
  }

  /**
   * Sets data vector.
   *
   * @param data the data
   * @param columnNames the column names
   */
  public void setDataVector(Object[][] data, Object[] columnNames) {
    setDataVector(convertToVector(data), convertToVector(columnNames));
  }

  /**
   * Sets the column identifiers.
   *
   * @param columnIdentifiers the column identifiers
   */
  public void setColumnIdentifiers(Vector columnIdentifiers) { // NOPMD
    this.columnIdentifiers = columnIdentifiers;
    if (columnIdentifiers == null) {
      setColumnCount(0);
    } else {
      setColumnCount(columnIdentifiers.size());
    }
  }

  /**
   * Sets the column identifiers.
   *
   * @param columnIdentifiers the column identifiers
   */
  public void setColumnIdentifiers(Object[] columnIdentifiers) {
    setColumnIdentifiers(convertToVector(columnIdentifiers));
  }

  /**
   * Returns <tt>true</tt>.
   *
   * @param row the row
   * @param column the column
   *
   * @return <tt>true</tt>
   */
  public boolean isCellEditable(int row, int column) {
    return true;
  }

  /**
   * Returns the value of the specified cell.
   *
   * @param row the row
   * @param column the column
   *
   * @return the value of the specified cell
   */
  public Object getValueAt(int row, int column) {
    Vector vector = (Vector)dataVector.get(row); // NOPMD
    return vector.get(column);
  }

  /**
   * Sets the value for the specified cell.
   *
   * @param value the value
   * @param row the row
   * @param column the column
   */
  public void setValueAt(Object value, int row, int column) {
    Vector vector = (Vector)dataVector.get(row); // NOPMD
    vector.set(column, value);
    fireTableCellUpdated(row, column);
  }

  /**
   * Adds column.
   *
   * @param columnName the column name
   * @param columnData the column data
   */
  public void addColumn(Object columnName, Object[] columnData) { // NOPMD
    int dvs = dataVector.size();
    if (columnData != null) {
      int cdl = columnData.length;
      if (cdl > dvs) {
        for (int i = cdl - dvs - 1; i >= 0; i--) {
          Vector vector = new Vector(); // NOPMD
          vector.setSize(columnIdentifiers.size());
          dataVector.add(vector);
        }
        dvs = dataVector.size();
      } else if (cdl < dvs) {
        Object[] ncd = new Object[dvs];
        System.arraycopy(columnData, 0, ncd, 0, cdl);
        columnData = ncd;
      }
    }
    if (columnData == null) {
      for (int i = 0; i < dvs; i++) {
        ((Vector)dataVector.get(i)).add(null); // NOPMD
      }
    } else {
      for (int i = 0; i < dvs; i++) {
        ((Vector)dataVector.get(i)).add(columnData[i]); // NOPMD
      }
    }
    columnIdentifiers.add(columnName);
    fireTableStructureChanged();
  }

  /**
   * Adds column.
   *
   * @param columnName the column name
   */
  public void addColumn(Object columnName) {
    addColumn(columnName, (Object[])null);
  }

  /**
   * Adds column.
   *
   * @param columnName the column name
   * @param columnData the column data
   */
  public void addColumn(Object columnName, Vector columnData) { // NOPMD
    Object[] dataArray = null;
    if (columnData != null) {
      if (columnData.size() < dataVector.size()) {
        columnData.setSize(dataVector.size());
      }
      dataArray = columnData.toArray();
    }
    addColumn(columnName, dataArray);
  }

  /**
   * Adds row.
   *
   * @param rowData the row data
   */
  public void addRow(Vector rowData) { // NOPMD
    int rowIndex = dataVector.size();
    dataVector.add(rowData);
    fireTableChanged(new TableModelEvent(this, rowIndex, rowIndex, -1,
                                         TableModelEvent.INSERT));
  }

  /**
   * Adds row.
   *
   * @param rowData the row data
   */
  public void addRow(Object[] rowData) {
    addRow(convertToVector(rowData));
  }

  /**
   * Inserts row.
   *
   * @param row the row.
   * @param rowData the row data.
   */
  public void insertRow(int row, Vector rowData) { // NOPMD
    dataVector.add(row, rowData);
    fireTableRowsInserted(row, row);
  }

  /**
   * Inserts row.
   *
   * @param row the row.
   * @param rowData the row data.
   */
  public void insertRow(int row, Object[] rowData) {
    insertRow(row, convertToVector(rowData));
  }

  /**
   * Removes row.
   *
   * @param row the row.
   */
  public void removeRow(int row) {
    dataVector.remove(row);
    fireTableRowsDeleted(row, row);
  }

  /**
   * Moves the rows.
   *
   * @param startIndex the start row
   * @param endIndex the end row
   * @param toIndex the destination row
   */
  public void moveRow(int startIndex, int endIndex, int toIndex) {
    Vector removed = new Vector(); // NOPMD
    for (int i = endIndex; i >= startIndex; i--) {
      removed.add(dataVector.remove(i));
    }
    int difIndex = endIndex - startIndex;
    for (int i = 0; i <= difIndex; i++) {
      dataVector.insertElementAt(removed.get(i), toIndex);
    }
    int firstRow = Math.min(startIndex, toIndex);
    int lastRow = Math.max(endIndex, toIndex + difIndex);
    fireTableRowsUpdated(firstRow, lastRow);
  }

// Protected Static Methods. 0--------------------------------------------------

  /**
   * Converts the data array to the vector.
   *
   * @param data the data array
   *
   * @return vector
   */
  protected static Vector convertToVector(Object[] data) { // NOPMD
    if (data != null) {
      int dl = data.length;
      Vector vector = new Vector(dl); // NOPMD
      for (int i = 0; i < dl; i++) {
        vector.add(data[i]);
      }
      return vector;
    }
    return null;
  }

  /**
   * Converts the data array to the vector of rows.
   *
   * @param data the data array
   *
   * @return vector of rows
   */
  protected static Vector convertToVector(Object[][] data) { // NOPMD
    if (data != null) {
      int dl = data.length;
      Vector vector = new Vector(dl); // NOPMD
      for (int i = 0; i < dl; i++) {
        vector.add(convertToVector(data[i]));
      }
      return vector;
    }
    return null;
  }

}
